﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Modbus;
using Modbus.Device;
using Modbus.Data;
using System.IO;
using System.IO.Ports;
using System.Threading;
using modbusslave;
using System.Timers;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {

        ModbusSlave slave;
        DataStore data50 = DataStoreFactory.CreateDefaultDataStore();
        DataStore data49 = DataStoreFactory.CreateDefaultDataStore();
        DataStore data51 = DataStoreFactory.CreateDefaultDataStore();
        DataStore data52 = DataStoreFactory.CreateDefaultDataStore();
        InputDataList data = new InputDataList("input.csv");
        StreamWriter log;
        Thread t1;
        int m_count = 1;
        SerialPort m_sonarOut;
        modBusTest.modbusmap mapToSonar;
        System.Timers.Timer timer = new System.Timers.Timer();

        //done is set by timer process when it is done will cause close process not to do the same
        bool done = false;

        private object lockObject = new object();
        bool m_main_communication_done;
        public Form1()
        {   
            InitializeComponent();
            try
            {
                progressBar1.Maximum = data.storage.Count;
                progressBar1.Minimum = 0;
                progressBar1.Step = 1;
                this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
                max.Text = progressBar1.Maximum.ToString();
                t1 = new Thread(new ThreadStart(PerformCommunication));
                mapToSonar = new modBusTest.modbusmap();
                mapToSonar.readDataXml("input.xml");
                m_sonarOut = new SerialPort("COM5", 9600, Parity.None, 8, StopBits.One);
                log = new StreamWriter("log.txt");

                if (!m_sonarOut.IsOpen)
                    m_sonarOut.Open();


                SetUpTimer();
                SetupReadAFC();
                m_main_communication_done = false;






                t1.Start();
                timer.Start();
            }
            catch (Exception err)
            {
                MessageBox.Show("Exception Caught in main thread: " + err.Message);

                if (err.GetType() == typeof(ThreadAbortException))
                {
                    Thread.ResetAbort();
                }
                //Application.Exit();
            }
            
            //PerformCommunication();
        }

        void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!done)
            {
                t1.Abort();
                timer.Stop();
                ((IDisposable)log).Dispose();
            }
        }

        private void PerformCommunication()
        {
            try
            {
                var key = from C in mapToSonar.storage
                          select C.paramName;


                log.Write("Index," + string.Join(",", key));
                log.WriteLine();

            tryportagain:
                using (SerialPort slavePort = new SerialPort("COM2"))
                {
                    // configure serial port
                    slavePort.BaudRate = 9600;
                    slavePort.DataBits = 8;
                    slavePort.Parity = Parity.None;
                    slavePort.StopBits = StopBits.One;
                    try
                    {
                        slavePort.Open();
                    }
                    catch (Exception err)
                    {
                        MessageBox.Show(err.Message);
                        goto tryportagain;
                    }
                    byte unitID49 = 49;
                    byte unitID50 = 50;
                    byte unitID51 = 51;
                    byte unitID52 = 52;

                    // create modbus slave
                    Dictionary<byte, DataStore> map = new Dictionary<byte, DataStore>();




                    map.Add(unitID49, data49);
                    map.Add(unitID50, data50);
                    map.Add(unitID51, data51);
                    map.Add(unitID52, data52);




                    slave = ModbusSerialSlave.CreateRtu(map, slavePort);




                    updateRegisters(data.storage[0]);
                    log.Write("Setting to index 0 at {0},", DateTime.Now.ToString());

                    this.Invoke((MethodInvoker)delegate { min.Text = m_count.ToString(); progressBar1.PerformStep(); });
                    //ModbusSlave slave2 = ModbusSerialSlave.CreateRtu(unitID, slavePort);

                    //data49.InputRegisters[100] = (ushort)100;
                    //data50.InputRegisters[200] = (ushort)200;
                    //slave.DataStore = DataStoreFactory.CreateDefaultDataStore(); 

                    //slave2.DataStore = DataStoreFactory.CreateDefaultDataStore();

                    //slave.DataStore.HoldingRegisters[0] = 0;
                    //slave.DataStore.HoldingRegisters[1] = 1;
                    //slave.DataStore.HoldingRegisters[2] = 2;
                    //slave.DataStore.HoldingRegisters[3] = 3;

                    //slave2.DataStore.HoldingRegisters[1] = 2;
                    //slave2.DataStore.HoldingRegisters[2] = 4;
                    //slave2.DataStore.HoldingRegisters[3] = 6;
                    ////slave.ModbusSlaveRequestReceived += new EventHandler<ModbusSlaveRequestEventArgs>(slave_ModbusSlaveRequestReceived);


                    //Thread t1 = new Thread(new ThreadStart(delegate() { slave.Listen(); }));
                    //Thread t2 = new Thread(new ThreadStart(delegate() { slave2.Listen(); }));
                    //Monitor.Exit(lockObject);

                    lock (lockObject)
                    {
                        m_main_communication_done = true;
                    }

                    //Monitor.PulseAll(lockObject);
                    slave.Listen();



                }
            }
            catch (Exception err)
            {
                using (StreamWriter outHandle = new StreamWriter("Error.txt"))
                {

                    outHandle.WriteLine(err.StackTrace);
                    outHandle.WriteLine(err.Message);
                    //outHandle.WriteLine(err.InnerException.Message);
                    //outHandle.WriteLine(err.InnerException.StackTrace);
                }
                MessageBox.Show("Exception caught in thread: " + err.Message + " " + err.GetType().ToString() + " " +  typeof(ThreadAbortException).ToString());


               

                //MessageBox.Show(err.GetType().ToString());
                //Thread.ResetAbort();

                if (err.GetType() == typeof(ThreadAbortException))
                {
                //    MessageBox.Show("Resetting exception");
                    //Thread.ResetAbort();
                }

                return;
            }
        }

        private void SetupReadAFC()
        {
            mapToSonar.master = ModbusSerialMaster.CreateRtu(m_sonarOut);
            mapToSonar.slaveID = 5;

            mapToSonar.m_floatFormat = modBusTest.modbusmap.floatFormat.normal;


        }

        private void SetUpTimer()
        {
            timer.AutoReset = false;
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);

            timer.Interval = 10000;
            timer.Enabled = false;
        }

        
        void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            lock (lockObject)
            {
                if (!m_main_communication_done)
                {
                    return;
                }
            }


            timer.Stop();
            var outputValues  = from C in mapToSonar.storage
                                select C.value;
            if (m_count > data.storage.Count - 1)
            {
                mapToSonar.getDataFromDevice();

                outputValues = from C in mapToSonar.storage
                                   select C.value;

                log.Write(string.Join(",", outputValues));
                log.WriteLine();

                
                //log.Write("Setting to index {0} at {1},", m_count, DateTime.Now.ToString());
                ((IDisposable)log).Dispose();
                this.Invoke((MethodInvoker)delegate { this.progressBar1.PerformStep(); min.Text = m_count.ToString(); });
                MessageBox.Show("Done");
                timer.Stop();

                //try
                //{
                //    t1.Abort();
                //}
                //catch (Exception err)
                //{
                //    MessageBox.Show(err.Message);
                //    Thread.ResetAbort();
                //}
                slave.stop = true;
                done = true;
                return;
                //Application.Exit();
                
            }
            mapToSonar.getDataFromDevice();

            outputValues = from C in mapToSonar.storage
                               select C.value;

            log.Write(string.Join(",",outputValues));
            log.WriteLine();

            //MessageBox.Show("Reading Data");
            this.Invoke((MethodInvoker)delegate { this.progressBar1.PerformStep(); min.Text = m_count.ToString(); });
            updateRegisters(data.storage[m_count]);
            log.Write("Setting to index {0} at {1},", m_count, DateTime.Now.ToString());
            //log.WriteLine();
            m_count++;
            
            timer.Start();
            
        }

        void updateRegisters(InputData data)
        {
            
            

            

            // water cut value ID = 49
            lock (data49.SyncRoot)
            {
                updateDataStoreInputRegistersFloat(data49, 1, data.Water_Cut);
            }

            // update sonar ID=50
            lock (data50.SyncRoot)
            {
                updateDataStoreInputRegistersFloat(data50, 17, data.Velocity);
                updateDataStoreInputRegistersFloat(data50, 25, data.Quality_factor);
            }

            //update mvt ID=51
            lock (data51.SyncRoot)
            {
                updateDataStoreHoldingRegistersFloat(data51, 401, data.DP);
                updateDataStoreHoldingRegistersFloat(data51, 403, data.Pressure);
                updateDataStoreHoldingRegistersFloat(data51, 405, data.Temperature);
            }
            //update densitomiter ID=52
            lock (data52.SyncRoot)
            {
                data52.HoldingRegisters[30] = data.Scaled_Density;
            }
        }

        void updateDataStoreHoldingRegistersFloat(DataStore dataStore, int address, float value)
        {
            byte[] all = BitConverter.GetBytes(value);

            //if t1 or t2 is slected this needs to be flipped
            dataStore.HoldingRegisters[address] = BitConverter.ToUInt16(all, 2);
            dataStore.HoldingRegisters[address + 1] = BitConverter.ToUInt16(all, 0);
        }

        void updateDataStoreInputRegistersFloat(DataStore dataStore, int address, float value)
        {
            byte[] all = BitConverter.GetBytes(value);

            //if t1 or t2 is slected this needs to be flipped
            dataStore.InputRegisters[address] = BitConverter.ToUInt16(all, 2);
            dataStore.InputRegisters[address + 1] = BitConverter.ToUInt16(all, 0);
        }


        void slave_ModbusSlaveRequestReceived(object sender, ModbusSlaveRequestEventArgs e)
        {
            //MessageBox.Show(e.Message.SlaveAddress.ToString());
            //Application.Exit();
        }
    }
}
